#include <WiFi.h>
#include <ArduinoWebsockets.h>
#include <ArduinoJson.h>
#include <base64.h>
#include <driver/i2s.h>

typedef enum
{
  RECORD_WAITING = 0,
  RECORDING,
  RECORD_FINISH,
} record_state_t;

#define I2S_IN_PORT I2S_NUM_0
#define I2S_IN_BCLK 39
#define I2S_IN_LRC 40
#define I2S_IN_DIN 38
const char *ssid = "zhouliangan44";
const char *password = "wxg13948532823";

static const size_t FRAME_SIZE = 1280;

using namespace websockets;
WebsocketsClient stt_client; // 与讯飞 STT 通信
int16_t *audioData = new int16_t[FRAME_SIZE];
int32_t *i2sData = new int32_t[FRAME_SIZE];

String Date;
String stt_url;
String askquestion;
size_t urlTime = 0;
String APPID = "565cdc82";                             // App ID, 必填
String APISecret = "MGFmMTU4MWNkOTM0NTViZjk1M2UxZDk1"; // API Secret，必填
String APIKey = "84937daeb42c357c67226209984acfd6";    // API Key，必填
String appId1 = APPID;
String stt_websockets_path = "ws://iat-api.xfyun.cn/v2/iat";
record_state_t record_s = RECORD_WAITING;

esp_err_t init(uint16_t sampling_rate)
{
  // Start listening for audio: MONO @ 8/16KHz
  i2s_config_t i2s_config = {
      .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX),
      .sample_rate = sampling_rate,
      .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
      .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
      .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S | I2S_COMM_FORMAT_I2S_MSB),
      .intr_alloc_flags = 0,
      .dma_buf_count = 8,
      .dma_buf_len = 512,
      .use_apll = false,
      .tx_desc_auto_clear = false,
      .fixed_mclk = -1,
  };
  i2s_pin_config_t pin_config = {
      .bck_io_num = I2S_IN_BCLK,
      .ws_io_num = I2S_IN_LRC,
      .data_out_num = -1,
      .data_in_num = I2S_IN_DIN};
  esp_err_t ret = 0;

  ret = i2s_driver_install(I2S_IN_PORT, &i2s_config, 0, NULL);
  if (ret != ESP_OK)
  {
    ESP_LOGE(TAG, "Error in i2s_driver_install");
  }

  ret = i2s_set_pin(I2S_IN_PORT, &pin_config);
  if (ret != ESP_OK)
  {
    ESP_LOGE(TAG, "Error in i2s_set_pin");
  }

  ret = i2s_zero_dma_buffer(I2S_IN_PORT);
  if (ret != ESP_OK)
  {
    ESP_LOGE(TAG, "Error in initializing dma buffer with 0");
  }

  return (ret);
}

// 获取音频数据（由用户实现）
record_state_t readAudioData()
{
  /*
      In this api should judge someone is speech or not.
      Use calculateRMS() to judge.
      16000Hz, 16bit, single channel.
      1280*sizeof(int16_t) = 2560 bytes
      1280/16000*1000=80ms
      */
  size_t byte_to_read = FRAME_SIZE * sizeof(int32_t);
  size_t bytes_read = 0;
  i2s_read(I2S_IN_PORT, (char *)i2sData, byte_to_read, &bytes_read, portMAX_DELAY);
  if (byte_to_read != bytes_read)
  {
    Serial.println("ERROR! I2S RECORD FAILED!!!");
    return RECORD_FINISH;
  }
  for (int i = 0; i < FRAME_SIZE; i++)
  {
    audioData[i] = (i2sData[i] >> 16) & 0xffff;
  }
  // Serial.println("return from recording");

  return RECORDING;
}

// 拼接讯飞 WebSocket 鉴权参数
String getUrl(String Spark_url, String host, String path, String Date)
{
  String signature_origin = "host: " + host + "\n";
  signature_origin += "date: " + Date + "\n";
  signature_origin += "GET " + path + " HTTP/1.1";

  unsigned char hmac[32];
  mbedtls_md_context_t ctx;
  mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
  const size_t messageLength = signature_origin.length();
  const size_t keyLength = APISecret.length();

  mbedtls_md_init(&ctx);
  mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
  mbedtls_md_hmac_starts(&ctx, (const unsigned char *)APISecret.c_str(), keyLength);
  mbedtls_md_hmac_update(&ctx, (const unsigned char *)signature_origin.c_str(), messageLength);
  mbedtls_md_hmac_finish(&ctx, hmac);
  mbedtls_md_free(&ctx);

  String signature_sha_base64 = base64::encode(hmac, sizeof(hmac) / sizeof(hmac[0]));

  Date.replace(",", "%2C");
  Date.replace(" ", "+");
  Date.replace(":", "%3A");

  String authorization_origin = "api_key=\"" + APIKey + "\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"" + signature_sha_base64 + "\"";
  String authorization = base64::encode(authorization_origin);

  String url = Spark_url + '?' + "authorization=" + authorization + "&date=" + Date + "&host=" + host;

  Serial.println(url);
  return url;
}

// 从服务器获取时间
void getTimeFromServer()
{
  String timeurl = "https://www.baidu.com";
  HTTPClient http;
  http.begin(timeurl);
  const char *headerKeys[] = {"Date"};
  http.collectHeaders(headerKeys, sizeof(headerKeys) / sizeof(headerKeys[0]));
  int httpCode = http.GET();
  Date = http.header("Date");
  Serial.println(Date);
  http.end();
}

// 接收讯飞 STT 返回的语音识别文本并处理逻辑
void sttOnMessageCallback(WebsocketsMessage message)
{

  Serial.println("on message!");

  DynamicJsonDocument jsonDocument(4096);
  DeserializationError error = deserializeJson(jsonDocument, message.data());

  if (error)
  {
    Serial.println("Error:");
    Serial.println(error.c_str());
    Serial.println(message.data());
    return;
  }

  Serial.println(message.data());
  if (jsonDocument["code"] != 0)
  {
    Serial.println(message.data());
    stt_client.close();
  }
  else
  {
    JsonArray ws = jsonDocument["data"]["result"]["ws"].as<JsonArray>();

    if (jsonDocument["data"]["status"] != 2)
    {
      askquestion = "";
    }

    for (JsonVariant i : ws)
    {
      for (JsonVariant w : i["cw"].as<JsonArray>())
      {
        askquestion += w["w"].as<String>();
      }
    }

    Serial.println(askquestion);

    if (jsonDocument["data"]["status"] == 2)
    {
      Serial.println("status == 2");
      Serial.println("stt service close");
      record_s = RECORD_FINISH;
      stt_client.close();
    }
  }
}

void onEventTask(void *pvParameters)
{
  int firstFrame = 1;
  DynamicJsonDocument doc(2000); // JSON 文档对象
  while (record_s == RECORDING)
  {
    readAudioData();

    doc.clear(); // 清空 JSON 文档

    JsonObject data = doc.createNestedObject("data");
    if (firstFrame == 1)
    {
      // 第一帧数据
      data["status"] = 0;
      firstFrame = 0;

      // 添加通用参数
      JsonObject common = doc.createNestedObject("common");
      common["app_id"] = appId1.c_str();

      // 添加业务参数
      JsonObject business = doc.createNestedObject("business");
      business["domain"] = "iat";
      business["language"] = "zh_cn"; // 示例语言
      business["accent"] = "mandarin";
      business["dwa"] = "wpgs"; // 动态修正
      business["vad_eos"] = 2500;
    }
    else
    {
      // 后续帧数据
      data["status"] = 1;
    }

    // 编码音频数据
    data["format"] = "audio/L16;rate=16000";
    data["audio"] = base64::encode((byte *)audioData, FRAME_SIZE * sizeof(int16_t));
    data["encoding"] = "raw";

    // 将 JSON 转为字符串并发送
    String jsonString;
    serializeJson(doc, jsonString);
    // Serial.println(jsonString);
    if (stt_client.send(jsonString) == false)
      Serial.println("Failed to send  data!");

    // delay(40);  // 模拟实时传输，每次发送 40ms 数据块
  }

  // 发送结束标志
  doc.clear();
  JsonObject data = doc.createNestedObject("data");
  data["status"] = 2; // 结束标志
  data["format"] = "audio/L16;rate=16000";
  data["audio"] = ""; // 空音频数据
  data["encoding"] = "raw";

  String endJsonString;
  serializeJson(doc, endJsonString);
  if (!stt_client.send(endJsonString))
    Serial.println("FAILED TO SEND DATA");

  Serial.println("Audio data sent!");
  vTaskDelete(NULL);
}

// WebSocket 事件回调
void sttOnEventCallback(WebsocketsEvent event,String data)
{
  
  if (event == WebsocketsEvent::ConnectionOpened)
  {
    record_s = RECORDING;
    xTaskCreate(onEventTask, "call back task", 4 * 1024, NULL, 10, NULL);
    Serial.println("event call back start");
  }
  else if (event == WebsocketsEvent::ConnectionClosed)
  {
    Serial.println("Connection Closed");
  }
  else if (event == WebsocketsEvent::GotPing)
  {
    Serial.println("Got a Ping!");
  }
  else if (event == WebsocketsEvent::GotPong)
  {
    Serial.println("Got a Pong!");
  }
  else
  {
    Serial.println("Failed!!!");
  }
}

// 连接到讯飞 WebSocket 服务器
void sttConnectServer()
{

  stt_client.onMessage(sttOnMessageCallback);
  stt_client.onEvent(sttOnEventCallback);

  if (stt_client.connect(stt_url.c_str()))
  {
    Serial.println("Connected to server1 (Xunfei STT)!");
  }
  else
  {
    Serial.println(stt_url+" Fialed");
  }
}

// 启动与讯飞服务器的对话
void StartConversation()
{
  // 如果距离上次时间同步超过4分钟
  if (urlTime + 240000 < millis())
  {
    // 更新时间戳
    urlTime = millis();
    // 从服务器获取当前时间
    getTimeFromServer();
    // 更新 WebSocket 连接的 URL
    stt_url = getUrl(stt_websockets_path, "iat-api.xfyun.cn", "/v2/iat", Date);
    Serial.println("stt_url" + stt_url);
  }
  // 连接到 WebSocket 服务器（讯飞 STT）
  sttConnectServer();
}

void setup()
{
  // 初始化串口通信，波特率为 115200
  Serial.begin(115200);

  // 初始化 WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("WiFi connected!");
  init(16000);

  urlTime = millis();
  // 从服务器获取当前时间
  getTimeFromServer();
  // 更新 WebSocket 连接的 URL
  stt_url = getUrl(stt_websockets_path, "iat-api.xfyun.cn", "/v2/iat", Date);
  Serial.println("stt_url" + stt_url);

 StartConversation();
}

void loop()
{
  // 轮询处理 WebSocket 客户端消息
  stt_client.poll();
  if (Serial.available())
  {
    String input = Serial.readStringUntil('\n'); //  \r表示结束符为回车符
    Serial.println(input);
    if (input.indexOf("1") == 0)
    {
      record_s = RECORD_FINISH;
      Serial.println((int)record_s);
    }
  }
  // Serial.println("loop");

  // readAudioData();
  // for(int i=0;i<FRAME_SIZE;i++)
  //   Serial.println(audioData[i]);
}
